﻿using bat_sym.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace bat_sym.SimulationLogic
{
    public class Simulation
    {
        private int _maxFoodLevel; //maximum ammount of food a bat can store
        private int _normalFoodLevel; //ammount of food needed for survival; everything above this can be shared
        private int _criticalFoodLevel; //ammount of food that triggers begging for more from peers
        private ObservableCollection<Bat> _bats;
        private ObservableCollection<ISimulationRule> _rules;
        private Timer _timer;
        private Statistics _statisticsManager;
        private Action _callback;
        public DrawingImage _drw;
        private PresetManager _presetManager;

        public Statistics StatisticsManager
        {
            get { return _statisticsManager; }
            set { _statisticsManager = value; }
        }

        public Simulation(int maxFoodLevel, int normalFoodLevel, int criticalFoodLevel, int batCount)
        {
            _maxFoodLevel = maxFoodLevel;
            _normalFoodLevel = normalFoodLevel;
            _criticalFoodLevel = criticalFoodLevel;

            _bats = new ObservableCollection<Bat>();
            for (int i = 0; i < batCount; i++)
            {
                _bats.Add(new Bat { Behaviour = BatBehaviour.Cheating, FoodAmmount = _maxFoodLevel, Label = "#" + i.ToString() });
            }

            _rules = new ObservableCollection<ISimulationRule>();
            _statisticsManager = new Statistics();
            _presetManager = new PresetManager();
        }

        public void StartSimulation()
        {
            _timer = new Timer(SimulateStep, null, 5, Timeout.Infinite);

            IsRunning = true;
        }

        public ObservableCollection<string> Presets
        {
            get
            {
                return _presetManager.Presets;
            }
        }

        public bool IsRunning { get; private set; }

        public void StopSimulation()
        {
            _timer.Dispose();
            _timer = null;
            IsRunning = false;
        }

        public void SimulateStep(object state)
        {
            Application.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                Step();
                var width = 1 ;
                var coop_ln = _statisticsManager._coop.Count;
                var comp_ln = _statisticsManager._comp.Count;
                var total_ln = _statisticsManager._total.Count;
                var avg_food_ln = _statisticsManager._avgFood.Count;
                var context = (_drw.Drawing as DrawingGroup).Append();
                if (coop_ln >= 2)
                {
                    context.DrawLine(new Pen(Brushes.Blue, width), _statisticsManager._coop[coop_ln - 2], _statisticsManager._coop[coop_ln - 1]);
                }

                if (comp_ln >= 2)
                {
                    context.DrawLine(new Pen(Brushes.Red, width), _statisticsManager._comp[comp_ln - 2], _statisticsManager._comp[comp_ln - 1]);
                }

                if (avg_food_ln >= 2)
                {
                    context.DrawLine(new Pen(Brushes.Orange, width), _statisticsManager._avgFood[avg_food_ln - 2], _statisticsManager._avgFood[avg_food_ln - 1]);
                }

                if (total_ln >= 2)
                {
                    context.DrawLine(new Pen(Brushes.Purple, width), _statisticsManager._total[total_ln - 2], _statisticsManager._total[total_ln - 1]);
                }

                if (_statisticsManager.iteration % 365 == 0)
                {
                    context.DrawLine(new Pen(Brushes.LightGray, 1d), new Point(_statisticsManager.iteration, 0), new Point(_statisticsManager.iteration, 300));
                    context.DrawText(new FormattedText("Year "+_statisticsManager.iteration/365, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("GenericSans"), 15, Brushes.LightGray), new Point(_statisticsManager.iteration-70, 150));
                }

                context.Close();
                if (IsRunning)
                {
                    _timer = new Timer(SimulateStep, null, 20, Timeout.Infinite);
                }
            }),System.Windows.Threading.DispatcherPriority.Background);
        }

        public void AddRule(ISimulationRule rule)
        {
            _rules.Add(rule);
        }

        public ObservableCollection<ISimulationRule> Rules
        {
            get
            {
                return _rules;
            }
        }

        public int MaxFoodLevel { get { return _maxFoodLevel; } }
        public int NormalFoodLevel { get { return _normalFoodLevel; } }
        public int CriticalFoodLevel { get { return _criticalFoodLevel; } }
        public ObservableCollection<Bat> Bats { get { return _bats; } }

        public void Step()
        {
            foreach (var rule in _rules)
            {
                if (rule.IsActive)
                {
                    rule.Execute(_bats);
                }
            }

            _statisticsManager.SaveStats(_bats);
            _callback.Invoke();
        }

        public void SetCallback(Action action, DrawingImage drw)
        {
            _callback = action;
            _drw = drw;
        }

        public void SaveCurrentPreset(string pName, int coops, int comps)
        {
            _presetManager.SavePreset(_rules, pName, coops, comps);
        }

        public dynamic LoadPreset(string pName)
        {
            return _presetManager.LoadPreset(_rules, pName);
        }

        public void DeletePreset(string pName)
        {
            _presetManager.DeletePreset(pName);
        }
    }
}
